package com.igoal.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.igoal.dao.CommentDAO;
import com.igoal.dao.GoalDAO;
import com.igoal.dao.PrivacySettingDAO;
import com.igoal.dao.UserActivityDAO;
import com.igoal.dao.UserDAOExt;
import com.igoal.dao.UserFriendDAO;
import com.igoal.dao.UserGoalEntryDAO;
import com.igoal.dao.UserGoalEntryDoneDAO;
import com.igoal.dao.UserLikeDAO;
import com.igoal.dao.UserStatusDAO;
import com.igoal.dao.UserThoughtDAO;
import com.igoal.dao.WallSettingDAO;
import com.igoal.dao.WallUpdateDAO;
import com.igoal.dto.CommentDTO;
import com.igoal.dto.FriendDTO;
import com.igoal.dto.UserActivityDTO;
import com.igoal.dto.UserActivityLogDTO;
import com.igoal.dto.WallActivityDTO;
import com.igoal.entity.ActionTypeEnum;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.Goal;
import com.igoal.entity.PrivacyEnum;
import com.igoal.entity.PrivacySetting;
import com.igoal.entity.User;
import com.igoal.entity.UserActivity;
import com.igoal.entity.UserFriend;
import com.igoal.entity.UserGoalDone;
import com.igoal.entity.UserGoalEntry;
import com.igoal.entity.UserInfo;
import com.igoal.entity.UserLike;
import com.igoal.entity.UserStatus;
import com.igoal.entity.UserThought;
import com.igoal.entity.WallPlace;
import com.igoal.entity.WallUpdate;
import com.igoal.entity.factory.EntityFactory;
import com.igoal.utils.StringUtils;
import com.igoal.web.Constants;

@Service("actionLoggerService")
public class ActionLoggerServiceImpl extends BaseService<UserActivityDTO, UserActivity, Long>
		implements ActionLoggerService {
	private final static Log logger = LogFactory.getLog(ActionLoggerServiceImpl.class);

	private UserActivityDAO userActivityDAO;
	private GoalDAO goalDAO;
	private UserDAOExt userDAOExt;
	private UserStatusDAO userStatusDAO;
	private UserThoughtDAO userThoughtDAO;
	private UserGoalEntryDAO userGoalEntryDAO;
	private UserGoalEntryDoneDAO userGoalEntryDoneDAO;
	private PrivacySettingDAO privacySettingDAO;
	private UserLikeDAO userLikeDAO;
	private CommentDAO commentDAO;
	private CommentService commentService;
	private UserFriendDAO userFriendDAO;
	private WallUpdateDAO wallUpdateDAO;
	private WallSettingDAO wallSettingDAO;

	@Autowired
	public void setUserActivityDAO(UserActivityDAO userActivityDAO) {
		this.userActivityDAO = userActivityDAO;
	}

	@Autowired
	public void setWallUpdateDAO(WallUpdateDAO wallUpdateDAO) {
		this.wallUpdateDAO = wallUpdateDAO;
	}

	@Autowired
	public void setWallSettingDAO(WallSettingDAO wallSettingDAO) {
		this.wallSettingDAO = wallSettingDAO;
	}

	@Autowired
	public void setUserGoalEntryDAO(UserGoalEntryDAO userGoalEntryDAO) {
		this.userGoalEntryDAO = userGoalEntryDAO;
	}

	@Autowired
	public void setUserGoalEntryDoneDAO(UserGoalEntryDoneDAO userGoalEntryDoneDAO) {
		this.userGoalEntryDoneDAO = userGoalEntryDoneDAO;
	}

	@Autowired
	public void setUserStatusDAO(UserStatusDAO userStatusDAO) {
		this.userStatusDAO = userStatusDAO;
	}

	@Autowired
	public void setGoalDAO(GoalDAO goalDAO) {
		this.goalDAO = goalDAO;
	}

	@Autowired
	public void setUserDAOExt(UserDAOExt userDAOExt) {
		this.userDAOExt = userDAOExt;
	}

	@Autowired
	public void setPrivacySettingDAO(PrivacySettingDAO privacySettingDAO) {
		this.privacySettingDAO = privacySettingDAO;
	}

	@Autowired
	public void setUserThoughtDAO(UserThoughtDAO userThoughtDAO) {
		this.userThoughtDAO = userThoughtDAO;
	}

	@Autowired
	public void setUserLikeDAO(UserLikeDAO userLikeDAO) {
		this.userLikeDAO = userLikeDAO;
	}

	@Autowired
	public void setCommentDAO(CommentDAO commentDAO) {
		this.commentDAO = commentDAO;
	}

	@Autowired
	public void setCommentService(CommentService commentService) {
		this.commentService = commentService;
	}

	@Autowired
	public void setUserFriendDAO(UserFriendDAO userFriendDAO) {
		this.userFriendDAO = userFriendDAO;
	}

	public boolean doesUserNeedLogThisInformation(User user, ActionTypeEnum actionType,
			ComponentEnum component) {
		return true;
	}

	public void log(User componentOwner, User actionOwner, ActionTypeEnum actionType,
			ComponentEnum component, Long componentId) {
		// log to wall_update
		PrivacySetting privacy = privacySettingDAO.getPrivacy(PrivacyEnum.PUBLIC);
		log(componentOwner, actionOwner, actionType, component, componentId, privacy);
	}

	public void log(User componentOwner, User actionOwner, ActionTypeEnum actionType,
			ComponentEnum component, Long componentId, PrivacySetting privacy) {
		if (!doesUserNeedLogThisInformation(componentOwner, actionType, component)) {
			return;
		}

		UserActivity log = EntityFactory.createUserActivity(componentOwner, actionOwner,
				actionType, component, componentId);
		log = userActivityDAO.save(log);

		// log to wall_update
		ComponentEnum wallComponent;
		Long wallComponentId;

		WallPlace wp = wallSettingDAO.getLikeAndCommentComponent(component, actionType);
		if (WallPlace.ACTIVITY.equals(wp)) {
			wallComponent = ComponentEnum.USER_ACTIVITY;
			wallComponentId = log.getId();
		} else {
			wallComponent = component;
			wallComponentId = componentId;
		}

		if (wallSettingDAO.showOnWall(component, actionType)) {
			wallUpdateDAO.updateToWall(componentOwner, wallComponent, wallComponentId, actionType,
					privacy);
		} else {
			wallUpdateDAO.updateToWall(componentOwner, wallComponent, wallComponentId, null,
					privacy);
		}
	}

	public List<UserActivityLogDTO> getNewActivitiesLog(String relatedUserName,
			boolean isExceptRelateUser, Date lastItemUpdateDate, boolean before) {
		List<UserActivityLogDTO> result = new ArrayList<UserActivityLogDTO>();
		try {
			User relatedUser = (User) userDAOExt.loadUserByUsername(relatedUserName);
			List<ActionTypeEnum> actionList = getActivitiesActionList();
			List<ComponentEnum> componentList = getActivitiesComponentList();

			Set<UserFriend> userFriends = relatedUser.getFriends();
			List<User> ignoreComponentOwners = null;
			List<User> actionOwners = new ArrayList<User>();
			for (UserFriend userFriend : userFriends) {
				actionOwners.add(userFriend.getFrom());
			}

			if (!isExceptRelateUser) {
				actionOwners.add(relatedUser);
			} else {
				ignoreComponentOwners = new ArrayList<User>();
				ignoreComponentOwners.add(relatedUser);
			}

			if (isLoginUser()) {
				String loginUserName = getUserInSession();
				try {
					User loginUser = (User) userDAOExt.loadUserByUsername(loginUserName);
					if (actionOwners.contains(loginUser)) {
						actionOwners.remove(loginUser);
					}
					if(ignoreComponentOwners != null)
						ignoreComponentOwners.add(loginUser);
				} catch (Exception e) {
					logger.error(e);
				}
			}

			List<UserActivity> logs = userActivityDAO.listActivities(ignoreComponentOwners,
					actionOwners, actionList, componentList, "updateDate", lastItemUpdateDate,
					Constants.MAX_USER_NOTIFICATION, before);

			for (UserActivity log : logs) {
				UserActivityLogDTO dto = new UserActivityLogDTO();
				dto.initFrom(log);
				try {
					if (ComponentEnum.GOAL.equals(log.getComponent())) {
						Goal goal = goalDAO.get(log.getComponentId());
						if (goal != null) {
							dto.setComponentTitle(goal.getTitle());
						}
					} else if (ComponentEnum.DONE_ENTRY.equals(log.getComponent())) {
						UserGoalDone entry = userGoalEntryDoneDAO.get(log.getComponentId());
						dto.setComponentTitle(entry.getUserGoal().getGoal().getTitle());
					} else if (ComponentEnum.GOAL_ENTRY.equals(log.getComponent())) {
						UserGoalEntry entry = userGoalEntryDAO.get(log.getComponentId());
						dto.setComponentTitle(entry.getUserGoal().getGoal().getTitle());
					} else if (ComponentEnum.USER_STATUS.equals(log.getComponent())) {
						UserStatus userStatus = userStatusDAO.get(log.getComponentId());
						if (userStatus != null) {
							dto.setComponentTitle(userStatus.getUserStatus());
						}
					}
				} catch (Exception e) {
					logger.error(e);
				}
				result.add(dto);
			}
		} catch (Exception e) {
			logger.error(e);
		}

		return result;
	}

	public List<UserActivityDTO> getNewNotificationsLog(String ownerName, Date lastItemUpdateDate,
			boolean before) {
		List<UserActivityDTO> result = new ArrayList<UserActivityDTO>();
		try {
			User owner = (User) userDAOExt.loadUserByUsername(ownerName);
			List<ActionTypeEnum> actionList = getNotificationActionList();
			List<ComponentEnum> componentList = getNotifiationComponentList();

			List<UserActivity> logs = userActivityDAO.listNotificationsLog(owner, null, actionList,
					componentList, "updateDate", lastItemUpdateDate,
					Constants.MAX_USER_NOTIFICATION, before);

			for (UserActivity log : logs) {
				UserActivityDTO dto = new UserActivityDTO();
				dto.initFrom(log);
				result.add(dto);
			}
		} catch (Exception e) {
			logger.error(e);
		}

		return result;
	}

	public List<ActionTypeEnum> getActivitiesActionList() {
		List<ActionTypeEnum> actionList = new ArrayList<ActionTypeEnum>();
		actionList.add(ActionTypeEnum.FINISH_GOAL);
		actionList.add(ActionTypeEnum.UPDATE);
		actionList.add(ActionTypeEnum.BE_FRIEND_WITH);
		actionList.add(ActionTypeEnum.NEW);
		actionList.add(ActionTypeEnum.CHEER);
		actionList.add(ActionTypeEnum.ENCOURAGE);
		// actionList.add(ActionTypeEnum.LIKE);

		return actionList;
	}

	public List<ComponentEnum> getActivitiesComponentList() {
		List<ComponentEnum> componentList = getNotifiationComponentList();
		componentList.add(ComponentEnum.FRIEND);
		componentList.add(ComponentEnum.GOAL);
		componentList.add(ComponentEnum.USER_STATUS);
		componentList.add(ComponentEnum.USER_AVATAR);

		return componentList;
	}

	public List<ActionTypeEnum> getNotificationActionList() {
		List<ActionTypeEnum> actionList = new ArrayList<ActionTypeEnum>();
		actionList.add(ActionTypeEnum.CHEER);
		actionList.add(ActionTypeEnum.ENCOURAGE);
		actionList.add(ActionTypeEnum.LIKE);
		// actionList.add(ActionTypeEnum.THUMBUP);
		actionList.add(ActionTypeEnum.COMMENT);
		actionList.add(ActionTypeEnum.NEW);

		return actionList;
	}

	public List<ComponentEnum> getNotifiationComponentList() {
		List<ComponentEnum> componentList = new ArrayList<ComponentEnum>();
		componentList.add(ComponentEnum.DONE_ENTRY);
		componentList.add(ComponentEnum.GOAL_ENTRY);
		componentList.add(ComponentEnum.USER_THOUGHT);
		// componentList.add(ComponentEnum.USER_STATUS);
		// componentList.add(ComponentEnum.USER_AVATAR);

		return componentList;
	}

	public int countNewNotificationLog(String ownerName, Date lastItemUpdateDate, boolean before) {
		try {
			User owner = (User) userDAOExt.loadUserByUsername(ownerName);
			List<ActionTypeEnum> actionList = getNotificationActionList();
			List<ComponentEnum> componentList = getNotifiationComponentList();

			return userActivityDAO.countNewNotificationLog(owner, null, actionList, componentList);
		} catch (Exception e) {
			logger.error(e);
		}
		return 0;
	}

	public List<WallActivityDTO> getWallActivities(String requestUserName, String wallOwnerName,
			Long createBefore) {
		User requestUser = (User) userDAOExt.loadUserByUsername(requestUserName);
		User wallOwner = (User) userDAOExt.loadUserByUsername(wallOwnerName);
		Date before = null;
		if (createBefore != null) {
			before = new Date(createBefore);
		}
		List<WallUpdate> activities = wallUpdateDAO.getWallActivities(requestUser, wallOwner,
				before);

		List<WallActivityDTO> result = new ArrayList<WallActivityDTO>();
		if (activities != null) {
			for (WallUpdate activity : activities) {
				WallActivityDTO dto = new WallActivityDTO();
				dto.initFrom(activity);

				ComponentEnum component = activity.getComponent();
				// ActionTypeEnum action = activity.getActionType();
				Long componentId = activity.getComponentId();
				getComponentText(dto, component, componentId);
				
				// TODO: review? wall_post
				if (ComponentEnum.USER_THOUGHT.equals(component)) {
					UserThought post = userThoughtDAO.get(componentId);

					String postOwner = post.getPostOwner().getUsername();
					UserInfo info = post.getPostOwner().getUserInfo();
					String postOwnerName = StringUtils.buildDisplayName(postOwner,
							info.getFirstName(), info.getMiddleName(), info.getLastName());
					dto.setPostOwner(postOwner);
					dto.setPostOwnerName(postOwnerName);
					dto.setPostOwnerAvatar(post.getPostOwner().getAvatar());
				}

				boolean isLikeEnabled = true;
				dto.setLikeEnabled(isLikeEnabled);
				if (isLikeEnabled) {
					ComponentEnum likeComponent = component;
					Long likeComponentId = componentId;
					List<UserLike> randomUserLike = userLikeDAO.getRandomeLikeList(
							requestUser.getId(), likeComponent, likeComponentId);
					List<FriendDTO> randomList = new ArrayList<FriendDTO>();
					for (UserLike like : randomUserLike) {
						FriendDTO u = new FriendDTO();
						u.initFrom(like.getUser());
						randomList.add(u);
					}

					dto.setLikeComponent(likeComponent.toString());
					dto.setLikeComponentId(likeComponentId);
					dto.setLikeThis(userLikeDAO.isLikedThis(requestUser.getId(), likeComponent,
							likeComponentId));
					dto.setRandomUserLikes(randomList);
					dto.setLikeCount(userLikeDAO.countComponentLike(likeComponent, likeComponentId));
				}

				boolean isCommentEnabled = true;
				dto.setCommentEnabled(isCommentEnabled);
				if (isCommentEnabled) {
					ComponentEnum commentComponent = component;
					Long commentComponentId = componentId;
					List<CommentDTO> comments = commentService.getLatestComments(
							commentComponentId, commentComponent);
					dto.setCommentComponent(commentComponent.toString());
					dto.setCommentComponentId(commentComponentId);
					dto.setLatestComments(comments);
					dto.setCommentCount(commentDAO.countComments(commentComponentId,
							commentComponent));
				}

				result.add(dto);
			}
		}

		return result;
	}

	private void getComponentText(WallActivityDTO dto, ComponentEnum component, Long componentId) {
		try {
			String text = null;
			String link = null;
			switch (component) {
				case GOAL:
					Goal goal = goalDAO.get(componentId);
					if (goal != null) {
						text = goal.getTitle();
						link = "/goal/view/" + goal.getId();
					}
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case DONE_ENTRY:
					UserGoalDone doneEntry = userGoalEntryDoneDAO.get(componentId);
					text = doneEntry.getTitle();
					link = "/entry/view/" + doneEntry.getId();
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case GOAL_ENTRY:
					UserGoalEntry entry = userGoalEntryDAO.get(componentId);
					text = entry.getTitle();
					link = "/entry/view/" + entry.getId();
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case USER_STATUS:
					UserStatus userStatus = userStatusDAO.get(componentId);
					if (userStatus != null) {
						text = userStatus.getUserStatus();
					}
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case USER_THOUGHT:
					UserThought userThought = userThoughtDAO.get(componentId);
					if (userThought != null) {
						text = userThought.getPost();
					}
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case FRIEND:
					UserFriend uf = userFriendDAO.get(componentId);
					User u = uf.getFrom();
					text = StringUtils.buildDisplayName(u.getUsername(), u.getUserInfo()
							.getFirstName(), u.getUserInfo().getMiddleName(), u.getUserInfo()
							.getLastName());
					link = "/" + u.getUsername();
					dto.setComponentTitle(text);
					dto.setComponentLink(link);
					break;
				case USER_ACTIVITY:
					UserActivity activity = userActivityDAO.get(componentId);
					getComponentText(dto, activity.getComponent(), activity.getComponentId());
					break;
				default:
					break;
			}
		} catch (Exception e) {
			logger.error(e);
		}
	}
}
